﻿using System;
using System.Collections.Generic;

namespace LightCAD.MathLib
{
    public class Utils
    {
        public const double EPSILON = 1E-06;
        public const double PI = Math.PI; //3.14159265358979323846;
        public const double TwoPI = Math.PI * 2;
        public const double HalfPI = Math.PI * 0.5;

        public static double Clamp(double value, double minv, double maxv)
        {
            return Math.Max(Math.Min(value, maxv), minv);
        }
        public static double AngelNormalize(double sweepAngle)
        {           
            while (sweepAngle< 0) sweepAngle += Utils.TwoPI;
            while (sweepAngle > Utils.TwoPI) sweepAngle -= Utils.TwoPI; 
            return sweepAngle;
		}

        /// <summary>
        /// 角度转弧度
        /// </summary>
        /// <param name="angle"></param>
        /// <returns></returns>
        public static double DegreeToRadian(double angle)
        {
            return angle * PI / 180.0;
        }

        /// <summary>
        /// 弧度转角度
        /// </summary>
        /// <param name="angle"></param>
        /// <returns></returns>
        public static double RadianToDegree(double angle)
        {
            return angle * 180.0 / PI;
        }

        public static bool IsEqual(double x, double y, double? epsilon = null)
        {
            if (epsilon == null)
                epsilon = EPSILON;
            return IsEqualZero(x - y, epsilon.Value);
        }

        public static bool IsEqualZero(double x, double epsilon = EPSILON)
        {
            return (Math.Abs(x) < epsilon);
        }

       /// <summary>
        /// 大于
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="eps"></param>
        /// <returns></returns>
        public static bool FloatGT(double a, double b, double? eps = null)
        {
            if (eps == null)
                eps = EPSILON;
            return (Math.Abs(a - b) >= eps) && a >= b;
        }
        /// <summary>
        /// 小于
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="eps"></param>
        /// <returns></returns>
        public static bool FloatLT(double a, double b, double? eps = null)
        {
            if (eps == null)
                eps = EPSILON;
            return (Math.Abs(a - b) > eps) && a < b;
        }
        /// <summary>
        /// 大于等于
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="eps"></param>
        /// <returns></returns>
        public static bool FloatGE(double a, double b, double? eps = null)
        {
            if (eps == null)
                eps = EPSILON;
            return a >= b || (Math.Abs(a - b) < eps);
        }


        /// <summary>
        /// 小于等于
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <param name="eps"></param>
        /// <returns></returns>
        public static bool FloatLE(double a, double b, double? eps = null)
        {
            if (eps == null)
                eps = EPSILON;
            return a <= b || (Math.Abs(a - b) < eps);
        }       
        public static bool Vec3EQ(Vector3 a, Vector3 b, double? eps = null)
        {
            if (eps == null)
                eps = EPSILON;
            return (Math.Abs(a.X - b.X) < eps) && (Math.Abs(a.Y - b.Y) < eps) && (Math.Abs(a.Z - b.Z) < eps);
        }

        public static bool Vec2EQ(Vector2 a, Vector2 b, double? eps = null)
        {
            if (eps == null)
                eps = EPSILON;
            return (Math.Abs(a.X - b.X) < eps) && (Math.Abs(a.Y - b.Y) < eps);
        }

        public static bool PlaneEQ(Plane a, Plane b, double neps = EPSILON, double ceps = EPSILON)
        {
            return Vec3EQ(a.Normal, b.Normal, neps) && IsEqual(a.Constant, b.Constant, ceps);
        }


        public static Vector2 IntersectLineLine(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2)
        {
            var ua_t = (b2.X - b1.X) * (a1.Y - b1.Y) - (b2.Y - b1.Y) * (a1.X - b1.X);
            var ub_t = (a2.X - a1.X) * (a1.Y - b1.Y) - (a2.Y - a1.Y) * (a1.X - b1.X);
            var u_b = (b2.Y - b1.Y) * (a2.X - a1.X) - (b2.X - b1.X) * (a2.Y - a1.Y);

            if (u_b != 0)
            {
                var ua = ua_t / u_b;
                var ub = ub_t / u_b;

                if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1)
                {
                    //Intersection
                    return new Vector2(
                            a1.X + ua * (a2.X - a1.X),
                            a1.Y + ua * (a2.Y - a1.Y)
                        );
                }
                else
                {
                    //None
                    return null;
                }
            }
            else if (ua_t == 0 || ub_t == 0) //Coincident
            {
                //Coincident
                return null;
            }
            else
            {
                //Parallel
                return null;
            }

            return null;
        }

        public static Vector2 LineIntersectLine2D(Vector2 p1, Vector2 dir1, Vector2 p2, Vector2 dir2, double eps = 0)
        {
            if (eps == 0)
                eps = EPSILON;
            if (IsEqual(dir1.Clone().Cross(dir2), 0, eps))
                return null;//共线
            if (IsEqual(dir1.Y, 0, eps) && IsEqual(dir2.X, 0, eps))
            {//两线正交垂直
                return new Vector2(p2.X, p1.Y);
            }
            else if (IsEqual(dir1.X, 0, eps) && IsEqual(dir2.Y, 0, eps))
            {//两线正交垂直
                return new Vector2(p1.X, p2.Y);
            }
            double a1 = dir1.Y, b1 = -dir1.X, c1 = p1.Y * dir1.X - p1.X * dir1.Y;
            double a2 = dir2.Y, b2 = -dir2.X, c2 = p2.Y * dir2.X - p2.X * dir2.Y;
            var d = a1 * b2 - a2 * b1;
            var x = (b1 * c2 - b2 * c1) / d;
            var y = (c1 * a2 - c2 * a1) / d;
            return new Vector2(x, y);
        }

        /// <summary>
        /// 生成顶点数组
        /// </summary>
        /// <param name="points"></param>
        /// <returns></returns>
        public static double[] GenerateVertexArr(List<Vector3> points)
        {
            var posArr = new double[points.Count * 3];
            for (int i = 0; i < points.Count; i++)
            {
                posArr[3 * i] = points[i].X;
                posArr[3 * i + 1] = points[i].Y;
                posArr[3 * i + 2] = points[i].Z;
            }
            return posArr;
        }


   
    }
}
